#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Author: Spayder26 < spayder26 _at_ gmail _dot_ com >
# License: GPLv3
#

from ConfigParser import SafeConfigParser
from base64 import b64decode
from os import system, getcwd, sep, getenv, linesep, sep, kill
from os.path import exists, isdir, isabs, expandvars, split as path_split
from commands import getoutput
from sys import argv, exit
from thread import start_new_thread, exit as thread_exit
from mimetypes import guess_type
from time import sleep
import getopt
from KThread import KThread as kthread
try:
    import pexpect
    from pxssh import *
    from pexpect import *
    from pxssh import __version__ as pxssh_version
    if int(round(float(pxssh_version)*100))<230:
        print "Your python pexpect module is outdated.",
        exit(2)
except:
    print "You need pexpect (python-pexpect) 2.3 or newer."
    exit(2)

__version__ = '0.2'
__all__ = ['pxsshY', 'ping', 'scp', 'ssh', 'send_files_and_add_to_transmission', 'ssh_to_transmission_gui', 'verify_files',
    '__version__']

class pxssh2(pxssh):
    """pexpect's pxssh module but allows X window aplications"""
    def __init__(self):
        pxssh.__init__(self)
        self.logged = False
        self.flag = False

    def login (self,server,username,password='',terminal_type='ansi',original_prompt=r"[#$]",login_timeout=10,X=False,port=None,auto_prompt_reset=True):
        while self.flag:
            sleep(0.5)
        self.flag = True
        ssh_options = '-q'
        if self.force_password:
            ssh_options = ssh_options + ' ' + self.SSH_OPTS
        if port is not None:
            ssh_options = ssh_options + ' -p %s'%(str(port))
        cmd = "ssh %s%s -l %s %s" % ("-X "*int(X),ssh_options, username, server)

        spawn._spawn(self, cmd)
        i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT, "(?i)connection closed by remote host"], timeout=login_timeout)

        if i==0:
            self.sendline("yes")
            i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT])
        if i==2: # password or passphrase
            self.sendline(password)
            i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT])
        if i==4:
            self.sendline(terminal_type)
            i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT])
        self.flag = False
        # Second phase
        if i==0:
            self.close()
            raise ExceptionPxssh ('Weird error. Got "are you sure" prompt twice.')
        elif i==1: # can occur if you have a public key pair set to authenticate.
            pass
        elif i==2: # password prompt again
            self.close()
            raise ExceptionPxssh ('password refused')
        elif i==3: # permission denied -- password was bad.
            self.close()
            raise ExceptionPxssh ('permission denied')
        elif i==4: # terminal type again? WTF?
            self.close()
            raise ExceptionPxssh ('Weird error. Got "terminal type" prompt twice.')
        elif i==5: # Timeout
            pass
        elif i==6: # Connection closed by remote host
            self.close()
            raise ExceptionPxssh ('connection closed')
        else: # Unexpected
            self.close()
            raise ExceptionPxssh ('unexpected login response')
        if not self.synch_original_prompt():
            self.close()
            raise ExceptionPxssh ('could not synchronize with original prompt')

        if auto_prompt_reset:
            if not self.set_unique_prompt():
                self.close()
                raise ExceptionPxssh ('could not set shell prompt\n'+self.before)

        return True

    def __auxtimeout(self,ufo=False):
        """Check ONLY login timeout"""
        if ufo:
            sleep(10)
            print " > Checking if logged..."
            if self.logged == False:
                print " > > Not logged, exiting..."
                self.close(True)
            else:
                print " > > OK"
            thread_exit()
        else:
            start_new_thread(self.__auxtimeout,(),{"ufo":True})
            
class scpinstance(object):
    def __init__(self,ip="", user="", passw="",port=22,sustitutive=None,mimetype="unknown",remotepath="",localpath=""):
        self.pid = -1
        self.usercancel = False
        self.remotepath = remotepath
        self.localpath = localpath
        self.sustitutive = sustitutive
        self.mimetype = mimetype
        self.__ip = ip
        self.__user = user
        self.__passw = passw
        self.__port = port
        self.__start()
        
    def restart(self):
        if self.child.isalive():
            self.close(force=True)
        self.usercancel = False
        self.__start()
    
    def __start(self):
        comando = "scp -qrP "+str(self.__port)+" "+self.__user+"@"+self.__ip+":\"'"
        comando += self.remotepath.replace('"','\\"').replace("'","\\'") + "'\" \""
        comando += self.localpath.replace('"','\\"') + "\""
        #print comando
        self.child = pexpect.spawn(comando,timeout=None)
        self.pid = self.child.pid
        self.child.expect('.ssword:*')
        self.child.sendline(self.__passw)
        self.__thread = self.__scpgetend()
        
    def exitstatus(self):
        return (self.child.exitstatus,self.child.signalstatus)
                
    def isalive(self):
        return self.child.isalive()
        
    def cancel(self):
        self.usercancel = True
        self.close(True)
    
    def close(self,force=False):
        self.__thread.kill()
        self.child.close(force=force)
        
    def __scpgetend(self,ufo=False):
        if ufo:
            self.child.read()
            self.close()
        else:
            a = kthread(target=self.__scpgetend,kwargs={"ufo":True})
            a.start()
            return a
           
def ping(ip,ping_num=1,ping_timeout=1):
    """Verifica que el host esté disponible, retorna True o False"""
    print "Checking connection to ",ip,"..."
    if getoutput("ping "+ip+" -c %d -W %d" % (ping_num, ping_timeout)).split("\n")[-2].find(", 0 received,")!=-1:
        print " > Host is unreachable."
        return False
    else:
        print " > OK"
        return True

def verify_files(filelist = []):
    """Verifica la existencia de la lista de archivos, y devuelve sus rutas completa."""
    files = []
    into = False
    for i in filelist:
        if not into:
            if i[0] in ("\"","'"):
                into = True
                i = i[1:]
            i = i.replace("\"","\\\"")
            if not isabs(i):
               i = getcwd() + sep + i
            if exists(i) or into:
               files.append(i)
            else:
               print "Error, %s doesn't exist." % i
        else:
            files[-1] += " i"
            if i[-1] in ("\"","'"):
                into = False
                if not exists(files[-1]):
                    aux = files.pop()
                    print "Error, %s doesn't exist." % aux
    return files

def scp(ip,user,passw,port=22,dest_folder="/tmp",files=[]):
    """Envía archivos via SCP (SSH) a la carpeta remota especificada."""
    new_files = []
    if len(files)>0:
        files_txt = ""
        for i in files:
            files_txt += "\""+i+"\" "
            new_files.append(dest_folder+sep+path_split(i)[1])
        files_txt = files_txt[:-1]
        print "Sending files (%s)..." % files_txt
        if dest_folder[-1]!="/":
            dest_folder += "/"
        comando = 'scp -q -P '+str(port)+' '+files_txt+' '+user+'@'+ip+':'+dest_folder
        #print " > Command: %s" % comando
        child = pexpect.spawn(comando)
        child.expect('.ssword:*')
        child.sendline(passw)
        child.read()
        child.close(force=True)
        print " > Done."
    else:
        print "No files to send via scp."
    return new_files
    
def send_to_transmission(ip="", user="", passw="",port=22,files=[],options="",session=None):
    if session==None:
        s = ssh(ip,user,passw,port,logout=False)[1]
    else:
        s = session
    
    files = verify_files(files)
    i = 0
    while i < len(files):
        if guess_type(files[i])[0]=="application/x-bittorrent":
            i += 1
        else:
            files.pop(i)
            print "Error, %s isn't a bittorrent file." % files[i]
    
    new_files = scp(ip,user,passw,port,dest_folder="/tmp",files=files)
    tosend = []
    command = "transmission-remote"
    if len(options)>0:
        command += " "+options.replace("transmission-remote ","")
    command += " -a"
    for i in new_files:
        tosend.append(command+" \""+i+"\"")
        tosend.append("rm \""+i+"\"")
    
    
    output = ssh(commands=tosend,session=s,logout=bool(session==None))
    
    if output[0]==1 or (session==None and output[0]==-1):
        return True
    return False
        
def ssh(ip="", user="", passw="",port=22,commands=[], connect_display=False,session=None,logout=True):
    """Initialize and executes via ssh sessions.
       - Returns: [state,session,output_list]
       - State means :
           -1 - Session closed.
            0 - cannot connect
            1 - OK
            2 - cannot login
            3 - bad session
            4 - cannot send commands to session    
    """
    output = []
    ok = True
    state = None
    ver =  pxssh_version
    if ver=="2.1":
        excepcion = ExceptionPexpect
    elif ver=="2.3":
        excepcion = ExceptionPxssh

    if session==None:
        try:
            s = pxssh2()
        except excepcion, e:
            state = 0
            ok = False
        try:
            if not s.login (server=ip, username=user, password=passw, port=port, X=connect_display):
                state = 2
                ok = False
        except:
            state = 2
            ok = False

    else:
        if not type(session) == pxssh2:
            state = 3
            ok = False
        else:
            s = session

    if ok:
        if s.isalive():
            try:
                if len(commands)>0:
                    separator1 = "db63e5a1d309865cc47ea96a606d5fe5"
                    separator2 = "38d5f0294827d631988217951d8dec2d"
                    tosend = "echo "+separator2
                    for i in commands[:-1]:
                        if type(i)!=str:
                            i = "echo None"
                        tosend += "&&" + i + "&&echo "+separator1
                            
                    tosend += "&&" + commands[-1]
            
                    #print " > Executing commands."
                    #print tosend
                    s.sendline(tosend)
                    s.prompt()
                    output = s.before
                    output = output.replace("\r\n",linesep).replace("\r",linesep).replace("\n",linesep).strip()
                    output = output.split(separator2)[-1].split(separator1)
                    #print " > Done."
            except Exception,e:
                print Exception,e
                state = 4
        else:
            state = 4
    
    if logout:
        s.logout()
        s.close()
        print " > Session closed."
        state = -1
        
    if state == None and ok:
        state = 1
    
    return [state,s,output]

ip=""
port=22
user=""
passw=""
dest_folder="/tmp"
